#ifndef _string_inc_string_h_
#define _string_inc_string_h_

#include "common/assert.h"
#include <cstring>
#include <cstdlib>

// Eventually, an actual UTF8 String implementation will appear here...
// Now it's a wrapper to a c string. Don't put '\0' in there or it
// will break.
struct String
{
	String()
		: m_contents(0)
		, m_length(0)
	{
	}

private:
	void init_from_c_str(const char* c_str)
	{
		ASSERT_MSG(c_str != 0, "Trying to initialize String with a null pointer");
			
		m_length = (int) std::strlen(c_str);
		m_contents = new char[m_length+1];
		std::strcpy(m_contents, c_str);
	}

public:
	String(const char* c_str)
	{
		init_from_c_str(c_str);
	}

	String(const String& other)
	{
		init_from_c_str(other.c_str());
	}

	explicit String(int initial_length)
		: m_contents(0)
		, m_length(0)
	{
		length(initial_length);
//		m_length = initial_length;
//		m_contents = new char[m_length];
	}

	String& operator=(const String& other)
	{
		deinit();
		init_from_c_str(other.c_str());

		return *this;
	}

private:
	void deinit()
	{
		delete[] m_contents;
	}

public:

	~String()
	{
		deinit();
	}

	// Accessors
	int length() const
	{
		if (m_contents == 0)
			return 0;
		else
			return m_length;
	}

	char* data()
	{
		return m_contents;
	}

	// Modifiers
	void clear()
	{
		length(0);
	}

private:
	void realloc(int length, char filler)
	{
		char* buf = new char[length+1];

		if (m_contents != 0)
			std::memcpy(buf, m_contents, this->length());

		int rest_length = length - this->length();
		// Set the rest to filler
		std::memset(
			buf + this->length(),
			filler,
			rest_length);
		// And terminate
		buf[length] = '\0';
		delete[] m_contents;
		m_contents = buf;

		m_length = length;
	}

public:
	void length(int length, char filler = '\0')
	{
		ASSERT(length >= 0);

		if (length > m_length)
		{
			realloc(length, filler);
		}
		else
		{
			m_length = length;
			if (m_contents != 0)
				m_contents[length] = '\0';
		}
	}

	// Operators
	char& operator[](int i)
	{
		ASSERT(i >= 0);
		ASSERT(i < length());

		return m_contents[i];
	}

	char operator[](int i) const
	{
		ASSERT(i >= 0);
		ASSERT(i < length());

		return m_contents[i];
	}

	const char* c_str() const
	{
		if (m_contents == 0)
			return "";

		return m_contents;
	}

	bool operator==(const String& other) const
	{
		int result = std::strcmp(c_str(), other.c_str());
		return result == 0;
	}

	bool operator!=(const String& other) const
	{
		return !(*this == other);
	}

	bool operator<(const String& other) const
	{
		int result = std::strcmp(c_str(), other.c_str());
		return result < 0;
	}

	String operator+(const String& other) const
	{
		int this_length = this->length();
		int other_length = other.length();

		String result(this_length + other_length);

		for (int i = 0; i < this_length; ++i)
			result[i] = (*this)[i];
		for (int i = 0; i < other_length; ++i)
		{
			result[this_length + i] = other[i];
		}

		return result;
	}

private:
	char* m_contents;
	int m_length;
};

#endif